Skip to content

feat(pm): H51 + H53 — the two contract-review carrier states no sweep reads, and the FAIL end-state H51 applies - #17138

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-16836-contract-review-fail-handoff-h51
Sep 9, 2026
Merged

feat(pm): H51 + H53 — the two contract-review carrier states no sweep reads, and the FAIL end-state H51 applies#17138
os-zhuang merged 2 commits into
mainfrom
claude/issue-16836-contract-review-fail-handoff-h51

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #16836
Fixes #16995

Two report-only patrol rows on the needs:contract-review gate, plus the one governed sentence that names the end-state the first of them tells a seat to apply. One file surface, two commits, one commit per card.

What each card asked for, and what landed

#16836 deliverable 1 — the FAIL end-state (governed). The PASS branch was precise (「PASS ⇒ 同席同笔剥双载体,清标同笔留 provenance 评论引该 PASS」); the FAIL branch said only 「转回相应工作态(重派/返工)」 into a state model with no rework state, so nothing said what the label set looks like after a FAIL. references/contract-review.md 载体纪律 now names it:

  • FAIL 同 PASS 剥双载体:同笔留卡上交接评论(引复审、独立性对、欠改);卡态与 assignee 不动。

#16836 deliverable 2 — H51. An OPEN PR carrying needs:contract-review whose own thread already holds a contract-review verdict for the current head, older than 60 minutes. Verdict-agnostic on purpose: both branches owe a label stroke inside the window, and a row that parsed the verdict would be issuing one.

#16995 half (1) — H53. An open card carrying the gate with no Claim: comment and no open or merged PR delivering it: 「carrier without increment」. H31-style and report-only — it never removes a carrier. Half (2) is #17007 and is not addressed here.

The one place the card's premise did not survive contact — H51's anchor

The card states the review-comment title as one literal format and calls the anchor structural on that basis. Measured against the live board it is not one format. Six contract-review comments across the seven gated open PRs, same day, in four shapes:

shape where the head sha is
## Contract review (…, isolated seat) — PR #N @ \sha`` in the heading (the card's cited form)
## Contract review (clause ②) — … · head \sha`` in the heading, other separator
## Contract review at \TIER` — Verdict: …` not in the heading — first body line
**Director seat adoption record** … + the review verbatim below the adoption sentence, and the embedded heading

A regex pinned to the card's literal would have been silent on two of the three dialects plus every adoption record, while reporting a clean board — a phantom check on more than half of its own population. So the row reads the two things all four shapes share and nothing else: a ## Contract review heading line (line-anchored with m, H48's MAINTAINER_BRIEF_MARKER register — body-anchoring would drop the adoption record, and 「逐字采纳」 is one of the two legal acts on a subagent verdict) and the head sha as a code span, tested as a case-insensitive prefix of the PR's head.sha. The looseness m admits — a comment merely quoting a heading — is answered by the second gate rather than by the regex: no head sha, no verdict on this head. Pinned as a case.

Live behaviour on the board at the time of writing, both directions firing:

PR head verdict on it verdict age row
#17061 9b4c0af7f416 yes 292 min finding
#17066 904c1dceb0da yes 309 min finding
#17090 ba3d95a4f351 yes 213 min finding
#17116 57421b4ae457 yes 80 min finding
#17073 4d15856e3e8e names an OLDER head clean (head moved)
#17115 05f5c96dfeb8 names an OLDER head clean (head moved)
#17126 9a52eaaa4438 none clean

H53 and the shape H31 deliberately declined

H31's header refuses 「gate label on a card with no PR carrier is premature」 because card-side-first is legal and expected (「报告先于 PR 到达则先挂卡侧」), and reporting it would flag the protocol's own prescribed sequence as a defect. The Claim: leg is what lets H53 read the half that premise excluded, and it is not decoration: a dev executing card-side-first has been dispatched, so its thread carries a claim. Claim present ⇒ silent. Claim absent and no PR ⇒ nobody is executing anything. With no PR there is no carrier pair at all, so H31 here is not clean but inapplicable — the row says so in its own sentence, and H31's live #10025 reading is pinned unchanged.

Bands, and why neither row is gate

Both are state. The gate band's own criterion is a gate whose absence reads as a green light, where 「被剥」 and 「从未挂过」 are indistinguishable — the reader H31/H35 are. Both new rows read the opposite direction: a carrier present and false. H51's nearest structural sibling is H48 (a verdict recorded, its second write missing, on an open PR, aged), which is state. H53 is ⛔ not stall either: whether a card-side carrier actually blocks dispatch is unmeasured — the filing thread says so and declines to grade on it — so the row does not claim the card is stopped.

Cost — measured, and the decision stated either way

PM mechanism assumption B asked whether the sweep already holds the threads. Measured in the script: it does not, for either row.

  • H51 reuses prCommentCache — H48's, whose header reserved exactly this ("a future PR-comment reader finds a thread this row already bought here and pays nothing for it"). Governed and gated are independent properties, so the marginal cost is one issue-comment walk per gated open PR that H48 never visited: 7 gated of 16 open PRs, single-page walks. The head-identity leg costs nothing at all — head.sha and labels both ride the open-PR list row already in hand.
  • H53 BUYS a complete card thread per gated open card, and that is a deliberate purchase. commentCache cannot serve it: H2 buys a thread only for an assigned card, and the measured population is unassigned by construction (a card nobody is executing has no assignee), so a cache-only reading would have reported the entire target shape UNJUDGED while looking perfectly healthy. Bounded by the gated population, not the open one: 9 gated of 599 open cards, single-page walks. Bought COMPLETE via H50's walk, because a full first page may hide the newest Claim: and reading "no claim" off a partial thread would invent the absence the row fires on.

Both coverage clauses render unconditionally and both keep UNJUDGED apart from clean.

Ablation — each new pin shown to fail

Self-restoring script, absolute paths, trap … EXIT INT TERM; every leg proves the mutation reached disk (target text count 1→0, injected text present, mutated blob hash ≠ the HEAD blob) and every restore leg is proven by git diff HEAD empty and the restored blob hash equal to HEAD's 74441227870ada406df6e207f81c1cf562dd1cd1.

leg (mutation) self-test named red cases
H51: accept any review comment, ignore the head sha exit 1 H51 clean: a review comment on an OLDER head; a comment MENTIONING a review heading…; 2 latestContractReviewOnHead cases — 4 of 2976
H51: remove the age gate exit 1 a verdict INSIDE the window is clean; one exactly AT it is clean too — 2 of 2976
H53: stop reading the thread for a Claim: exit 1 the same card once its thread carries a Claim: + 3 boundary cases — 4 of 2976
H53: stop excluding cards that already have a delivering PR exit 1 open-PR, merged-PR and Part of cases — 3 of 2976

⚠️ One honest weakness recorded rather than smoothed: leg 1's injected text (true,) occurs 80 times in the file, so its injection count is not a unique proof. The load-bearing half is — the target text went 1 → 0 and the blob hash moved.

Gates

Re-derived on this diff with no paths (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack) — identical set to the dispatch's lead, zero additions. All run, exit codes captured before any pipe:

✓ dispatch-gates --ran: 43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED.
✓ check-half-states self-test: 2976 cases pass.        (2926 before H53's 50)
✓ check-skill-line-ratchet: contract-review.md is 60 lines (ceiling 60; headroom 0).
✓ check-skill-line-ratchet: lanes/director.md is 72 lines (ceiling 72; headroom 0).
✓ dispatch-gates self-test: 1586 cases pass.           (check:pm-dispatch-gates, detached + `tail --pid`)

pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 — PREREQUISITE NOT MET, which its own text calls "not a finding"; the two packages it needs were built under the shared verify lock (VERDICT command-exit 0) and it then exited 0. Repo-wide lint is a real reading rather than a declared narrowing: eslint . --no-inline-config --format json over its own population of 6,427 files, 0 messages, run at 375c7961.

Ceilings — paid by deletion, ⛔ no raise, ⛔ no re-wrap

contract-review.md stays 60/60 and lanes/director.md 72/72. The new sentence is paid by deleting the line it subsumes — contract-review.md:47 「审计 FAIL 按状态机 label-flip 交回派发席补丁轮。」, which said the same thing without an end-state. ⛔ No merged bullets, ⛔ no cross-file move declared, ⛔ no ceiling touched. Every touched line is ≤120 bytes (new line: 120 B).

On the director.md mirror — PM assumption C's explicit fork, answered with a measurement. The card asks for the sentence mirrored in lanes/director.md 职责一. A second full copy is what the ratchet's own cross-file-move rationale calls the defect ("a fact can be in the wrong FILE … read by that lane and missed by the other six"), and the director charter already points at this single source twice (:29 「归属与全链见 ../contract-review.md」, :71 「../contract-review.md 为准」). So :36 is rewritten in place — both halves of assumption C's disjunction at once, the in-place rewrite and the pointer:

-- 之二,大的错误 FAIL 或 REWORK ⇒ 改标签即交接:按状态机一笔 replace 转回相应工作态。
+- 之二,大的错误 FAIL 或 REWORK ⇒ 改标签即交接,端态按 `../contract-review.md` 载体纪律。

The 之二 enumeration and the ⛔ 空交接 line at :37 are untouched — the latter is where 「只贴复审评论不改标签 = 空交接」 already lives, alongside state-machine.md's 「交接即标签」, so this PR does not add a third copy of it.

Governed terminal

.claude/** in the diff ⇒ draft PR, maintainer merge; ⛔ never ready, enqueued, auto-merged or approved by a seat. Verbatim (exit 3):

governed-surface predicate: 2 of 3 path(s) hit the register (5 surfaces, repo-agnostic).
  ⛔  GOVERNED — a human merge is the review record for this PR (#9495 regime).
      No seat flips it ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive #14).
      One hit governs the whole PR — 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question.
      .claude/** ×2 — the agent instruction tree (skills, agents, hooks, settings)
        - .claude/skills/pm-dispatch/references/contract-review.md
        - .claude/skills/pm-dispatch/references/lanes/director.md
  paths not on the register: scripts/pm/check-half-states.mjs

skip-changeset measured, not assumed: 0 of the repo's package manifests carry a files[] entry that could ship .claude/** or scripts/pm/** (positive control — packages/spec files[] reads ["dist","json-schema","liveness",…]).

维护者速读(草稿)

改了什么 — 契约复核闸门 needs:contract-review 上加两条只报告、不写标签的巡检行,外加一句治理文本。① H51:PR 还挂着闸门,而它自己的评论线程里已经有一份针对当前 head 的复核结论,超过 60 分钟没有任何标签动作。② H53:卡上挂着闸门,却既没有 Claim: 认领评论、也没有任何 PR —— 「有载体、无增量」。③ contract-review.md 补上 FAIL 的终态一句(此前只有 PASS 分支写清楚了)。

为什么改 — 一个班次里十二个 PR 拿到了复核结论,2 到 5 小时无人响应,最后是维护者先发现的(「已审 9 个 objectstack PR,为什么还是挂着待契约复审的 label」)。席位的巡检读标签,不读 PR 散文,所以只写成评论的结论谁也到不了。另一侧:裁定落地的第二天早上,同一个分诊席又预挂了 13 个空载体 —— 靠一次总监召唤才发现。两种都是「标签在说谎」,而没有任何机器读得出来。

风险与代价(含回滚) — 风险低:两行都是 report-only,绝不摘任何载体(摘除是有据可查的人的动作)。代价是每轮巡检多买少量评论线程 —— 7 个挂标 PR(与 H48 共用缓存)加 9 张挂标卡,各一页。行数棘轮未抬,靠删掉被取代的那一行付账。回滚 = revert 两个 commit,巡检回到今天的状态,不影响任何已发布物。

席位意见 — (留空,待席位定稿)

你要做的 — 这是受管面(.claude/**),按规矩只能您本人合并;席位不会转 ready、不入队、不挂 auto-merge。合并前值得看一眼的只有一处:contract-review.md 新增的那句 FAIL 终态,和 director.md 由「转回相应工作态」改成指向该单源的那一行。

Acceptance notes

🤖 Generated with Claude Code

https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ


Generated by Claude Code

… label stroke, and the FAIL end-state it applies

`needs:contract-review` is a dual-carrier gate whose verdict is a HANDOFF, and
the handoff's second act is a LABEL STROKE. Twelve PRs took a verdict in one
shift and no owning seat responded for 2-5 hours: the seats' sweeps read labels,
not PR prose, so a verdict recorded only as a comment reached nobody. H31 is
clean throughout — it compares the two carriers with each other, and both were
correctly on.

Two halves, both from the filing card:

- `references/contract-review.md` 载体纪律 names the FAIL end-state, which the
  text never did (the PASS branch was precise, the FAIL branch said only
  「转回相应工作态」 into a state model with no rework state). Paid in place by
  deleting the vague line it subsumes; ceiling untouched at 60/60.
  `lanes/director.md` 职责一 is rewritten in place to point at that single
  source rather than carry a second copy; 72/72 untouched.

- H51 files a report-only row for an OPEN gated PR whose thread already holds a
  contract-review verdict for the CURRENT head, older than 60 minutes. Verdict-
  agnostic on purpose: both branches owe a stroke inside the window, and a row
  that parsed the verdict would be issuing one. A verdict naming an OLDER head
  is clean — the head moved, so the carrier is genuinely live again.

The anchor is measured rather than quoted: three title dialects were live on the
same day plus a director adoption record carrying the review verbatim, so the
row reads the two things all four share — a `## Contract review` heading line
and the head sha as a code span. Pinning the card's literal title would have
been silent on two dialects while reporting a clean board.

Reuses H48's `prCommentCache`, whose header reserved exactly this reader;
`head.sha` and `labels` ride the open-PR list row, so head identity costs no
request. The H52 self-test's `H51` reservation pin flips to its landed side.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ
…m and no PR

The gate rides an increment: 「PR 一存在即挂;报告先于 PR 到达则先挂卡侧」 says
where it is hung, 「⛔ 不前瞻预挂」 says where it is not, and the invariant both
serve is 「开着的载体恒 = 真实待审」. Fourteen open cards across two repos
carried it with no `Claim:` comment and no PR at all — thirteen written the
morning after a ruling restating the discipline merged into the text that seat
reads. All were found by a director summon; no sweep could see them.

H31 needs two carriers to compare, so with no PR it is INAPPLICABLE rather than
clean, and its header deliberately declined the「gate on a card with no PR is
premature」 shape because card-side-first is legal. The CLAIM leg is what lets
this row read the half that premise excluded: a dev executing card-side-first
has been dispatched, so its thread carries a claim. Claim present ⇒ silent.

⛔ Report-only, in H31's own register — the subject is a GATE, the row never
removes a carrier and never asks a script to. Removal is a person's audited act.

It BUYS a complete card thread per gated open card rather than reading a cache:
the measured population is unassigned by construction and H2 buys a thread only
for an ASSIGNED card, so a cache-only reading would report the whole target
shape UNJUDGED while looking healthy. 9 gated of 599 open cards, single-page
walks. H31's live `#10025` reading is pinned unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ

Copy link
Copy Markdown
Collaborator Author

维护者速读 — PR #17138(#16836 + #16995 折叠,skills 席终稿,2026-09-09T12:38Z)

改了什么 — 契约复核闸门 needs:contract-review 上加两条只报告、不写标签的巡检行,外加治理文本各一行。① H51:PR 还挂着闸门,而它自己的评论线程里已有一份针对当前 head 的复核结论,超过 60 分钟没有任何标签动作(结论内容不读、不判 PASS/FAIL)。② H53:卡上挂着闸门,却既没有 Claim: 认领评论、也没有任何 PR 交付它——「有载体、无增量」;卡先挂载体、dev 已派发的合法序列有认领评论,不会被点名。③ contract-review.md 载体纪律补上 FAIL 的终态一句(FAIL 同 PASS 剥双载体、卡上留交接评论、卡态与 assignee 不动),删掉被它取代的旧句;director.md 职责一那行改成指向该单源。行数 60/60、72/72 不动,不抬上限。

为什么改 — 一个班次里十二个 PR 拿到复核结论后 2 到 5 小时无人响应,最后是你先发现的(「已审 9 个 objectstack PR,为什么还是挂着待契约复审的 label」);席位的巡检读标签不读 PR 散文,只写成评论的结论谁也到不了。另一侧:裁定落地的第二天早上,分诊席又预挂了 13 个空载体,靠一次总监召唤才发现。两种都是「标签在说谎」而没有机器读得出来。你在 #16836 上的原话「立一张 domain:skills 卡(一句终态 + 一条 H 检查)」,本 PR 就是那一句加那一条,并把 #16995 的同形行折叠进来。

风险与代价(含回滚) — 两行都是 report-only,永不摘任何载体(摘除是有据可查的人的动作);每轮巡检多买少量评论线程(7 个挂标 PR 与 H48 共用缓存,9 张挂标卡各买一页),未判定的明写 UNJUDGED 不冒充干净。dev 实测复核评论标题在同一天有四种写法,所以 H51 认标题行 + head sha 两样,四种都钉了用例——写侧要不要收敛成一种是协议问题,本 PR 不动。回滚 = revert 两个 commit。

席位意见 — 接受。本席在分支头上复核:三行治理文本原文如上、宽度 120/110 B、两份账本零余量不动;被删句在 SKILL.md :644 有原文;两条谓词读过,新增行里无任何标签/assignee/PR 写入;check-half-states --self-test 本地重跑 2976 全过;merge-tree 干净;CI 20 绿 / 11 跳过 / 3 在跑(12:35Z)。总监席留在 #16836 上的「Co-Authored-By 不是档位证据」那一句,已另立 #17139,不随本卡关闭而丢。

你要做的 — 看一眼 contract-review.md 新增的那句 FAIL 终态与 director.md 那行指针;CI 全绿后人工合并,或由 os-zhuang / hotlong 批准后本席入队(受管面,席位不翻 ready、不入队、不挂 auto-merge)。合并后本席按串行派 #16688#17017#17139


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants